import os
import re
import wx

from . import panel_xrc

from app.util import pluginBase, files, path

class ControlPanel(panel_xrc.xrcControlPanel, pluginBase.PluginMixin):
    """VNC control panel."""

    name = "Remote control"
    _vncServer = _vncPort = None
    _ledProcess = None

    _lockRe = re.compile(r"\.X(\d+)-lock")
    _portRe = re.compile(r"PORT=(\d+)\n")
    _ledStatusRe = re.compile(r"([A-Za-z_]+)\s+(\d+)\n")
    _ledEchoRe = re.compile(r"SAY SOMETHING\n")
    _ledStatuses = None

    _serverConnectTimer = _workTimer = None

    _ledRefreshTime = 10

    _ledReadScript = """\
    while [ 1 == 1 ] ; do \
        echo -n "aux_red ";\
        cat /sys/devices/platform/gta02-led.0/leds/gta02-aux\:red/brightness;\
        echo -n "power_blue ";\
        cat /sys/devices/platform/gta02-led.0/leds/gta02-power\:blue/brightness;\
        echo -n "power_orange ";\
        cat /sys/devices/platform/gta02-led.0/leds/gta02-power\:orange/brightness;\
        echo "SAY SOMETHING"
        unset script_input; \
        read -t %(refresh_time)i script_input;\
        if [ -z "${script_input}" ]; then \
            exit; \
        fi; \
        sleep %(refresh_time)i; \
    done\
    """

    _imgFolder = path.getModulePath("app.plugins.RemoteViewer.img")

    _ledPicts = {
        "aux_off": wx.Bitmap(os.path.join(_imgFolder, "aux_off.png")),
        "aux_on": wx.Bitmap(os.path.join(_imgFolder, "aux_on.png")),
        "power_off": wx.Bitmap(os.path.join(_imgFolder, "power_off.png")),
        "power_on_blue": wx.Bitmap(os.path.join(_imgFolder, "power_on_blue.png")),
        "power_on_orange": wx.Bitmap(os.path.join(_imgFolder, "power_on_orange.png")),
        "power_on_violet": wx.Bitmap(os.path.join(_imgFolder, "power_on_violet.png")),
    }

    def __init__(self, parent, app):
        super(ControlPanel, self).__init__(parent)
        self._mixinInit(parent, app)
        self._ledStatuses = {
            "aux_red":  False,
            "power_blue": False,
            "power_orange": False,
        }
        self.remoteControl.setLogMethod(
            lambda msg: app.log_debug("[RFB] " + msg))

        _timerId = wx.NewId()
        self._serverConnectTimer = wx.Timer(self, _timerId)
        self.Bind(
            wx.EVT_TIMER, self._OnServerTimer, self._serverConnectTimer)

        _timerId = wx.NewId()
        self._workTimer = wx.Timer(self, _timerId)
        self.Bind(
            wx.EVT_TIMER, self.OnWorkTimer, self._workTimer)

    def _setLedStatus(self, name, status):
        assert name in self._ledStatuses, name
        _statuses = self._ledStatuses
        if _statuses[name] == status:
            # don't do anyting if status didn't change
            # (avoinds image flickeruing)
            return
        # dicts are mutable!
        # (so, self._ledStatuses is updated too)
        _statuses[name] = status
        _auxState = "aux_on" if _statuses["aux_red"] else "aux_off"
        if _statuses["power_blue"] and _statuses["power_orange"]:
            _powerState = "power_on_violet"
        elif _statuses["power_blue"]:
            _powerState = "power_on_blue"
        elif _statuses["power_orange"]:
            _powerState = "power_on_orange"
        else:
            _powerState = "power_off"
        self.aux.SetBitmap(self._ledPicts[_auxState])
        self.power.SetBitmap(self._ledPicts[_powerState])

    def _stopServer(self):
        if self._vncServer:
            # TODO: find how to send CTRL-C to remote program
            self.remoteControl.detach()
            self._app.executeRemote("killall x11vnc")
            self._vncServer.shutdown()
            self._vncServer = None
        if self._ledProcess:
            # shell will autom automatically exit after timeout
            self._ledProcess = None

    def _startServer(self):
        if self._vncServer:
            self._stopServer()

        _p = self._app.executeRemote("which x11vnc")
        _p.wait()
        _found = _p.stdout.read().strip()
        if not _found:
            wx.MessageBox("Failed to find remote 'x11vnc' executable\r" \
                "Not installed 'x11vnc' package?", 'Info')
            return
        _displays = self._app.phone.xDisplays
        if not _displays:
            wx.MessageBox("Failed to find running X displays.", 'Info')
            return
        if len(_displays) > 1:
            self._app.log_info(
                "Found %s displays. Using first one." % _displays)
        _display = _displays[0]
        # FIXME: That's quite evil
        _p = self._app.executeRemote("killall x11vnc")
        _p.wait()
        self._vncServer = self._app.executeRemote(
            "x11vnc -once --display :%s" % _display)
        self._ledProcess = self._app.executeRemote(self._ledReadScript % {
            "refresh_time": self._ledRefreshTime,
        })
        self._serverConnectTimer.Start(milliseconds=300, oneShot=False)

    def OnTogglebutton_remCtrlAct(self, evt):
        if evt.Checked():
            self._startServer()
        else:
            self._stopServer()

    _serverOut = ""
    def _OnServerTimer(self, evt):
        # check if server is ready to accept connections
        if not self._vncServer:
            self._serverConnectTimer.Stop()
        self._vncServer.poll()
        self._serverOut += self._vncServer.stdout.read()
        _match = self._portRe.search(self._serverOut)
        if _match:
            self._vncPort = _match.group(1)
            # server is ready
            _sock = self._app.openRemotePort(int(self._vncPort))
            self.remoteControl.useSocket(_sock)
            self._serverOut = ""
            self._serverConnectTimer.Stop()
            self._workTimer.Start(milliseconds=500, oneShot=False)


    _ledOut = ""
    def OnWorkTimer(self, evt):
        if not self._vncServer or not self._ledProcess:
            self._stopServer()
            self._workTimer.Stop()
            return
        self._vncServer.poll()
        self._ledProcess.poll()
        _newData = self._ledProcess.stdout.read()
        if _newData:
            self._ledOut += _newData
            _match = self._ledStatusRe.search(self._ledOut)
            while _match:
                (_name, _status) = _match.groups()
                self._setLedStatus(_name, int(_status))
                self._ledOut = self._ledOut[_match.end():]
                _match = self._ledStatusRe.search(self._ledOut)
            # I assume that right now shell scripts waits for
            # input right now
            _match = self._ledEchoRe.search(self._ledOut)
            if _match:
                self._ledProcess.stdin.write("PING\n")
                self._ledOut = self._ledOut[_match.end():]

# vim: set sts=4 sw=4 et :
